﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IDAL;
using IChessEngine;
using System.Xml.Linq;
using ChessEngine.Gaming;
using ChsExceptions.GameEdit;
using ChessEngine.Basic;
using ChessEngine;
using System.Transactions;
using System.Xml;
using System.IO;

namespace DAL
{
    public class ChsXmlGameEditDAO : IChsGameEditDAO
    {
        string filename;
        XDocument document;
        public ChsXmlGameEditDAO(string _filename)
        {
            filename = _filename;
            if (File.Exists(filename))
                document = XDocument.Load(filename);
            else
            {
                document = new XDocument(new XElement("games"));
                Save();
            }
        }
        public List<IChsGameInfo> GetGames()
        {
            List<IChsGameInfo> returned = new List<IChsGameInfo>();
            var q = from c in document.Descendants("game")
                    select new ChsGameInfo ( (Guid)c.Attribute("id"),
                                 (string)c.Element("gameInfo").Element("whitePlayer").Attribute("name"),
                                 (string)c.Element("gameInfo").Element("blackPlayer").Attribute("name"),
                                 DateTime.Parse(c.Element("gameInfo").Element("date").Value));
            foreach (ChsGameInfo c in q)
            {
                returned.Add(c);
            }
            return returned;
        }
        public IChsGame GetGame(Guid gameId)
        {
            IChsGame returned;
            var gameInfo = from c in document.Descendants("game")
                    where (Guid)c.Attribute("id") == gameId
                    select ChsFactory.CreateGame((Guid)c.Attribute("id"),
                                 (string)c.Element("gameInfo").Element("whitePlayer").Attribute("name"),
                                 (string)c.Element("gameInfo").Element("blackPlayer").Attribute("name"),
                                 DateTime.Parse(c.Element("gameInfo").Element("date").Value));
            if (gameInfo.Count() == 0)
                throw new ChsGameNotFoundException();
            if (gameInfo.Count() > 1)
                throw new ChsRepeatedGameIdException();
            returned = gameInfo.First();
            var movements = from c in document.Descendants("movement")
                where (Guid)(c.Parent.Parent.Attribute("id")) == gameId
                select ChsFactory.CreateGameMovement(
                     new ChsSquare((int)(c.Element("source").Attribute("x")), (int)(c.Element("source").Attribute("y"))),
                     new ChsSquare((int)(c.Element("target").Attribute("x")), (int)(c.Element("target").Attribute("y"))),
                     (EChsPieceType)Enum.Parse(typeof(EChsPieceType), (string)c.Attribute("promoting")),
                     (Guid)c.Element("source").Attribute("position"),
                     (Guid)c.Element("target").Attribute("position"));
            foreach (ChsGameMovement gameMovement in movements)
            {
                returned.AddMovement(gameMovement);
            }
            return returned;
        }
        public void SaveGame(IChsGame game)
        {
            var elements = (from c in document.Descendants("game")
                     where (Guid)c.Attribute("id") == game.Id
                     select c);
            if (elements.Count() > 1)
                throw new ChsRepeatedGameIdException();
            XElement gameElement;
            XElement movementElements;
            if (elements.Count() == 0)
            {
                gameElement = new XElement("game");
                gameElement.Add(new XAttribute("id", game.Id));
                XElement gameInfoElement = new XElement("gameInfo");
                XElement blackPlayerElement = new XElement("blackPlayer");
                blackPlayerElement.Add(new XAttribute("name", game.BlackPlayer));
                gameInfoElement.Add(blackPlayerElement);
                XElement whitePlayerElement = new XElement("whitePlayer");
                whitePlayerElement.Add(new XAttribute("name", game.WhitePlayer));
                gameInfoElement.Add(whitePlayerElement);
                XElement dateElement = new XElement("date");
                dateElement.Value = game.Date.ToString();
                gameInfoElement.Add(dateElement);
                gameElement.Add(gameInfoElement);
                document.Element("games").Add(gameElement);
                movementElements = new XElement("movements");
                gameElement.Add(movementElements);
            }
            else
            {
                gameElement = elements.First();
                gameElement.Element("gameInfo").Element("whitePlayer").Attribute("name").Value = game.WhitePlayer;
                gameElement.Element("gameInfo").Element("blackPlayer").Attribute("name").Value = game.BlackPlayer;
                gameElement.Element("gameInfo").Element("date").Value = game.Date.ToString();
                movementElements = gameElement.Element("movements");
                movementElements.RemoveNodes();
            }
            foreach (IChsGameMovement movement in game.Movements)
            {
                XElement movementElement = new XElement("movement");
                movementElement.Add(new XAttribute("promoting", movement.PromotingType));
                XElement sourceElement = new XElement("source");
                sourceElement.Add(new XAttribute("x", movement.Source.X));
                sourceElement.Add(new XAttribute("y", movement.Source.Y));
                sourceElement.Add(new XAttribute("position", movement.SourcePositionId));
                movementElement.Add(sourceElement);
                XElement targetElement = new XElement("target");
                targetElement.Add(new XAttribute("x", movement.Target.X));
                targetElement.Add(new XAttribute("y", movement.Target.Y));
                targetElement.Add(new XAttribute("position", movement.TargetPositionId));
                movementElement.Add(targetElement);
                movementElements.Add(movementElement);
            }
        }
        public void Save()
        {
            using (XmlWriter writer = XmlWriter.Create(filename))
            {
                document.WriteTo(writer);
            }
        }
    }
}
